/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package abricots.entity.command;

import abricots.MyLog;
import abricots.command.Command;
import abricots.command.Commandable;
import abricots.entity.Entity;
import abricots.net.common.GameMessage.CommandsMessage;
import com.esotericsoftware.minlog.Log;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;

/**
 *
 * @author charly
 */
public class BasicEntityCommandManager implements EntityCommandManager, Commandable {
   protected final MyLog log = new MyLog(getClass().getSimpleName(), Log.LEVEL_DEBUG);
   protected final List<Command> commands;
   protected final CommandComparator commandComparator;
   protected final Entity entity;

   public BasicEntityCommandManager(Entity entity) {
      this.entity = entity;
      commands = Collections.synchronizedList(new LinkedList<Command>());
      commandComparator = new CommandComparator();
   }

   @Override
   public List<Command> getCommands() {
      synchronized (commands) {
         return new ArrayList<Command>(commands);
      }
   }

   @Override
   public CommandsMessage buildCommandMessage(long fromTime, long toTime) {
      CommandsMessage message = new CommandsMessage();
      message.fromTime = fromTime;
      message.toTime = toTime;
      message.commands = getCommandsFrom(fromTime);
      message.entityId = entity.getId();
      log.trace("Built command msg from " + fromTime + " to " + toTime);
      return message;
   }

   public List<Command> getCommandsFrom(long gameTime) {
      List<Command> subCommands = new ArrayList<Command>();
      synchronized (subCommands) {
         Iterator<Command> it = commands.iterator();
         while (it.hasNext()) {
            Command command = it.next();
            if (command.getGameTime() < gameTime) { //TODO: check < <= and co on commands!
               continue;
            }
            subCommands.add(command);
         }
      }
      return subCommands;
   }

   @Override
   public void clearAllBefore(long gameTime) {
      synchronized (commands) {
         Iterator<Command> it = commands.iterator();
         while (it.hasNext()) {
            Command command = it.next();
            if (command.getGameTime() < gameTime) {
               it.remove();
            } else {
               break;
            }
         }
      }
      log.trace("Command before " + gameTime + " discarded, " + commands.size() + " left");
   }

   @Override
   public void processCommand(Command command) {
      addCommand(command);
   }

   @Override
   public void addCommands(List<Command> commandList) {
      if (commandList == null) {
         return;
      }
      long lastCommandTime = getLastCommandTime();
      log.trace("Asked to add " + commandList.size() + " commands.");
      for (Command command : commandList) {
         // Only add new commands here, we might have received some of them already
         if (lastCommandTime > command.getGameTime()) {
            log.trace(" Last command for " + lastCommandTime + " more recent than " + command.getGameTime());
            continue;
         }
         if (lastCommandTime == command.getGameTime()) {
            boolean similarFound = false;
            synchronized (commands) {
               for (Command presentCommand : commands) {
                  if (presentCommand.getGameTime() != lastCommandTime) {
                     continue;
                  }
                  if (presentCommand.equals(command)) {
                     log.trace("A similar command already exists :" + command + ", discarding");
                     similarFound = true;
                     break;
                  }
               }
            }
            if (similarFound) {
               continue;
            }
         }
         addCommand(command);
      }
   }

   @Override
   public long getLastCommandTime() {
      long lastCommandTime = -1;
      synchronized (commands) {
         if (!commands.isEmpty()) {
            lastCommandTime = commands.get(commands.size() - 1).getGameTime();
         }
      }
      return lastCommandTime;
   }

   @Override
   public void addCommand(Command command) {
      synchronized (commands) {
         commands.add(command);
         Collections.sort(commands, commandComparator);
      }
      log.trace("Added command " + command.toString());
   }

   protected class CommandComparator implements Comparator<Command> {
      @Override
      public int compare(Command o1, Command o2) {
         return (int) (o1.getGameTime() - o2.getGameTime());
      }
   }
}
